package cn.csg.iotplatform.module.system.service.dept;

import cn.csg.iotplatform.framework.common.constants.GlobalConstants;
import cn.csg.iotplatform.framework.common.constants.RedisCacheKey;
import cn.csg.iotplatform.framework.common.enums.CommonStatusEnum;
import cn.csg.iotplatform.framework.common.pojo.PageResult;
import cn.csg.iotplatform.framework.common.util.common.ToolUtil;
import cn.csg.iotplatform.framework.common.util.object.BeanUtils;
import cn.csg.iotplatform.framework.datapermission.core.annotation.DataPermission;
import cn.csg.iotplatform.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.csg.iotplatform.framework.web.core.util.WebFrameworkUtils;
import cn.csg.iotplatform.module.system.controller.admin.dept.vo.dept.*;
import cn.csg.iotplatform.module.system.controller.admin.user.vo.user.UserDeptRelationVo;
import cn.csg.iotplatform.module.system.dal.dataobject.dept.*;
import cn.csg.iotplatform.module.system.dal.dataobject.dept.vo.DeptSearchVO;
import cn.csg.iotplatform.module.system.dal.dataobject.user.AdminUserDO;
import cn.csg.iotplatform.module.system.dal.mysql.dept.DeptMapper;
import cn.csg.iotplatform.module.system.dal.mysql.user.AdminUserMapper;
import cn.csg.iotplatform.module.system.dal.redis.RedisKeyConstants;
import cn.csg.iotplatform.module.system.enums.DeptTreeTypeEnum;
import cn.csg.iotplatform.module.system.mq.producer.dept.DeptProjectAttrChangeProducer;
import cn.csg.iotplatform.module.system.mq.producer.dept.DeptProjectAttrChangeProducer;
import cn.csg.iotplatform.module.system.service.permission.PermissionService;
import cn.csg.iotplatform.module.system.service.permission.RoleService;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.StopWatch;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.csg.iotplatform.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.csg.iotplatform.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.csg.iotplatform.module.system.enums.ErrorCodeConstants.*;

/**
 * 部门 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
@Slf4j
public class DeptServiceImpl implements DeptService {

    @Resource
    private DeptMapper deptMapper;
    @Resource
    private DeptProjectAttrChangeProducer deptProjectAttrChangeProducer;
    @Resource
    private AdminUserMapper userMapper;
    @Lazy
    @Resource
    private RoleService roleService;

    @Override
    @Transactional
    @CacheEvict(cacheNames = {RedisKeyConstants.DEPT_CHILDREN_ID_LIST, RedisCacheKey.SYSTEM_DEPT_TREE},
            allEntries = true) // allEntries 清空所有缓存，因为操作一个部门，涉及到多个缓存
    public Long createDept(DeptSaveReqVO createReqVO) {
        if (createReqVO.getParentId() == null) {
            createReqVO.setParentId(DeptDO.PARENT_ID_ROOT);
        }
        // 校验父部门的有效性
        DeptDO deptDO = validateParentDept(null, createReqVO.getParentId()); //返回父部门信息
        // 校验部门名的唯一性
        validateDeptNameUnique(null, createReqVO.getParentId(), createReqVO.getName());

        // 插入部门
        DeptDO dept = BeanUtils.toBean(createReqVO, DeptDO.class);
        deptMapper.insert(dept);

        //设置treePath,并且保存
        String endStr = "[" + dept.getId() + "],";
        dept.setTreePath(deptDO.getTreePath() + endStr);

        this.deptMapper.updateById(dept);

        return dept.getId();
    }

    @Override
    @CacheEvict(cacheNames = {RedisKeyConstants.DEPT_CHILDREN_ID_LIST, RedisCacheKey.SYSTEM_DEPT_TREE},
            allEntries = true) // allEntries 清空所有缓存，因为操作一个部门，涉及到多个缓存
    public void updateDept(DeptSaveReqVO updateReqVO) {
        if (updateReqVO.getParentId() == null) {
            updateReqVO.setParentId(DeptDO.PARENT_ID_ROOT);
        }
        // 校验自己存在
        validateDeptExists(updateReqVO.getId());
        // 校验父部门的有效性
        DeptDO deptDO = validateParentDept(updateReqVO.getId(), updateReqVO.getParentId());
        // 校验部门名的唯一性
        validateDeptNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName());

        // 更新部门
        DeptDO updateObj = BeanUtils.toBean(updateReqVO, DeptDO.class);
        //设置treePath,并且保存
        String endStr = "[" + updateObj.getId() + "],";
        updateObj.setTreePath(deptDO.getTreePath() + endStr);

        deptMapper.updateById(updateObj);
    }

    @Override
    @CacheEvict(cacheNames = {RedisKeyConstants.DEPT_CHILDREN_ID_LIST,RedisCacheKey.SYSTEM_DEPT_TREE},
            allEntries = true) // allEntries 清空所有缓存，因为操作一个部门，涉及到多个缓存
    public void deleteDept(Long id) {
        // 校验是否存在
        validateDeptExists(id);
        // 校验是否有子部门
        if (deptMapper.selectCountByParentId(id) > 0) {
            throw exception(DEPT_EXITS_CHILDREN);
        }
        // 删除部门
        deptMapper.deleteById(id);
    }

// -------------------------------------------------------------------------------------------------------------------------

    @VisibleForTesting
    void validateDeptExists(Long id) {
        if (id == null) {
            return;
        }
        DeptDO dept = deptMapper.selectById(id);
        if (dept == null) {
            throw exception(DEPT_NOT_FOUND);
        }
    }

    @VisibleForTesting
    DeptDO validateParentDept(Long id, Long parentId) {
        if (parentId == null || DeptDO.PARENT_ID_ROOT.equals(parentId)) {
            return null;
        }
        // 1. 不能设置自己为父部门
        if (Objects.equals(id, parentId)) {
            throw exception(DEPT_PARENT_ERROR);
        }
        // 2. 父部门不存在
        DeptDO parentDept = deptMapper.selectById(parentId);
        if (parentDept == null) {
            throw exception(DEPT_PARENT_NOT_EXITS);
        }
        // 3. 递归校验父部门，如果父部门是自己的子部门，则报错，避免形成环路
        if (id == null) { // id 为空，说明新增，不需要考虑环路
            return parentDept;
        }
        for (int i = 0; i < Short.MAX_VALUE; i++) {
            // 3.1 校验环路
            parentId = parentDept.getParentId();
            if (Objects.equals(id, parentId)) {
                throw exception(DEPT_PARENT_IS_CHILD);
            }
            // 3.2 继续递归下一级父部门
            if (parentId == null || DeptDO.PARENT_ID_ROOT.equals(parentId)) {
                break;
            }
            parentDept = deptMapper.selectById(parentId);
            if (parentDept == null) {
                break;
            }
        }
        return parentDept;
    }

    @VisibleForTesting
    void validateDeptNameUnique(Long id, Long parentId, String name) {
        DeptDO dept = deptMapper.selectByParentIdAndName(parentId, name);
        if (dept == null) {
            return;
        }
        // 如果 id 为空，说明不用比较是否为相同 id 的部门
        if (id == null) {
            throw exception(DEPT_NAME_DUPLICATE);
        }
        if (ObjectUtil.notEqual(dept.getId(), id)) {
            throw exception(DEPT_NAME_DUPLICATE);
        }
    }

    @Override
    public DeptDO getDept(Long id) {
        return deptMapper.selectDeptById(id);
    }

    @Override
    public DeptDO getDeptByOrgId(String orgId) {
        return deptMapper.selectOne("org_id", orgId);
    }

    @Override
    public DeptDO getDeptBySapHrOrgId(String sapHrOrgId, String eqOrgCode) {
        return deptMapper.selectOne(new LambdaQueryWrapperX<DeptDO>()
        .eq(DeptDO::getSapHrOrgId, sapHrOrgId).eq(DeptDO::getEqOrgCode, eqOrgCode));
    }


    @Override
    public List<DeptDO> getDeptList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return deptMapper.selectBatchIds(ids);
    }

    @Override
    public List<DeptDO> getDeptList(DeptListReqVO reqVO) {

        if ( ObjectUtil.isNotEmpty( reqVO ) ){

            List<DeptDO> list = deptMapper.selectList(reqVO);
            list.sort(Comparator.comparing(DeptDO::getSort));
            return list;
        }else {

            return deptMapper.selectDeptList();
        }

    }

    @Override
    public List<Map<String, Object>> getDeptList() {

        QueryWrapper<DeptDO> deptDOQueryWrapper = new QueryWrapper<>();
        deptDOQueryWrapper.eq("deleted", 0);


        return deptMapper.selectMaps( deptDOQueryWrapper );
    }

    @Override
    public List<DeptDO> getChildDeptList(Long id) {
//        List<DeptDO> children = new LinkedList<>();
        // 遍历每一层
//        Collection<Long> parentIds = Collections.singleton(id);
//        for (int i = 0; i < Short.MAX_VALUE; i++) { // 使用 Short.MAX_VALUE 避免 bug 场景下，存在死循环
//            // 查询当前层，所有的子部门
//            List<DeptDO> depts = deptMapper.selectListByParentId(parentIds);
//            // 1. 如果没有子部门，则结束遍历
//            if (CollUtil.isEmpty(depts)) {
//                break;
//            }
//            // 2. 如果有子部门，继续遍历
//            children.addAll(depts);
//            parentIds = convertSet(depts, DeptDO::getId);
//        }
//        return children;
        return deptMapper.selectChildDeptList(DeptSearchVO.builder().useFlag(1).build());
    }

    @Override
    @DataPermission(enable = false) // 禁用数据权限，避免建立不正确的缓存
    @Cacheable(cacheNames = RedisKeyConstants.DEPT_CHILDREN_ID_LIST, key = "#id")
    public Set<Long> getChildDeptIdListFromCache(Long id) {
        List<DeptDO> children = getChildDeptList(id);
        return convertSet(children, DeptDO::getId);
    }

    @Override
    public void validateDeptList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        // 获得科室信息
        Map<Long, DeptDO> deptMap = getDeptMap(ids);
        // 校验
        ids.forEach(id -> {
            DeptDO dept = deptMap.get(id);
            if (dept == null) {
                throw exception(DEPT_NOT_FOUND);
            }
            if (!CommonStatusEnum.ENABLE.getStatus().equals(dept.getStatus())) {
                throw exception(DEPT_NOT_ENABLE, dept.getName());
            }
        });
    }

    @Override
    public void validateDeptName(String value) {
        if (ToolUtil.isEmpty(value)) {
            throw exception(DEPT_IS_EMPTY);
        }
        log.info("传入用户单位名：{}",value);
        List<DeptDO> deptDOS = this.deptMapper.selectList(new LambdaQueryWrapperX<DeptDO>()
                .eq(DeptDO::getName, value)
                .eq(DeptDO::getDeleted, 0));
        if (ToolUtil.isEmpty(deptDOS)) {
            throw exception(DEPT_NOT_FOUND);
        }
    }

    @Override
    public List<Tree<Object>> getDeptTree(DeptTreeDto deptTreeDto, Boolean isUse) {
        DeptDO deptDO = this.deptMapper.selectById(deptTreeDto.getId()); //校验请求合法性
        if (ToolUtil.isEmpty(deptDO)) {
            return new ArrayList<>();
        }
        List<DeptDO> sourceDataList = new ArrayList<>();
        //1.查询所需数据
        LambdaQueryWrapperX<DeptDO> queryWrapper = new LambdaQueryWrapperX<>();
        if (isUse) queryWrapper.eq(DeptDO::getUseFlag, 1);
        queryWrapper.eq(DeptDO::getStatus, 0);
        queryWrapper.ne(DeptDO::getId, deptTreeDto.getId());
        if (ToolUtil.isNotEmpty(deptTreeDto.getId()))
            queryWrapper.like(DeptDO::getTreePath, "[" + deptTreeDto.getId() + "]");
        //if (ToolUtil.isNotEmpty(deptTreeDto.getName())) queryWrapper.like(DeptDO::getName, deptTreeDto.getName());
        List<DeptDO> dataList = this.deptMapper.selectList(queryWrapper);
        //2.构建结构树
        sourceDataList.add(deptDO);
        sourceDataList.addAll(dataList);
        List<Tree<Object>> trees = new ArrayList<>();
        if (ToolUtil.isNotEmpty(sourceDataList)) {
            trees = TreeUtil.build(sourceDataList, deptDO.getParentId(), null, (dept, tree) -> {
                tree.setId(dept.getId());
                tree.setParentId(dept.getParentId());
                tree.setWeight(dept.getSort());
                tree.setName(dept.getName());
                if (!isUse) tree.putExtra("useFlag", dept.getUseFlag());
            });
        }
        return trees;
    }

    @Override
    public List<Tree<Object>> get4ADeptTree(Long id) {
        return getDeptTree(new DeptTreeDto(), false);
    }

    @Override
    public PageResult<DeptWithParentDO> getDeptListWithParent(DeptListReqVO reqVO) {
        IPage<DeptWithParentDO> page = new Page<>(reqVO.getPageNo(), reqVO.getPageSize());
        deptMapper.selectDeptListWithParent(page, reqVO);
        return new PageResult<>(page.getRecords(), page.getTotal());
    }

    @Override
    @CacheEvict(cacheNames = {RedisCacheKey.SYSTEM_DEPT_TREE},
            allEntries = true)
    public void batchSave(List<DeptListSaveReqVO> list) {
        List<DeptDO> doList = new ArrayList<>();
        list.forEach(e -> doList.add(BeanUtils.toBean(e, DeptDO.class)));
        deptMapper.updateBatch(doList);
        deptProjectAttrChangeProducer.sendDeptProjectAttrChangeMessage(false,doList.stream().map(DeptDO::getId).collect(Collectors.toList()));
    }

    @Override
    @CacheEvict(cacheNames = {RedisKeyConstants.DEPT_CHILDREN_ID_LIST, RedisCacheKey.SYSTEM_DEPT_TREE},
            allEntries = true)
    public void batchUpdateUseFlag(List<Long> ids, Integer useFlag) {
        List<Long> idsAll = new ArrayList<>();
        if(useFlag == 0){
            ids.forEach(e->idsAll.addAll(getChildDeptIdsByUseFlag(e,1)));
        }else{
            idsAll.addAll(ids);
        }
        for(int i=0,size=idsAll.size();i<size;i=i+5000){
            deptMapper.batchUpdateUseFlag(idsAll.subList(i,i+5000 > size?size:i+5000), useFlag);
        }
        if(useFlag == 0){
            deptProjectAttrChangeProducer.sendDeptProjectAttrChangeMessage(true,ids);
        }
    }

    private List<Long> getChildDeptIdsByUseFlag(Long deptId,Integer useFlag) {
        return deptMapper.selectChildDeptIdsByUserFlag(deptId,useFlag);
    }

    @Override
    public List<AdminAreaDO> selectAdminAreaSetting(Long deptId) {
        return deptMapper.selectAdminAreaSetting(deptId);
    }

    @Override
    public List<AdminAreaDO> getAdminAreaList(Integer type,String provinceCode) {
        return deptMapper.selectAdminAreaList(type,provinceCode);
    }

    @Override
    @Transactional
    public Boolean saveDeptAreaSetting(Long deptId, Integer type, List<String> codes) {
        deptMapper.deleteDeptAreaRel(deptId);
        List<DeptAreaRelDO> list = Lists.newArrayList();
        codes.forEach(e->list.add(DeptAreaRelDO.builder().deptId(deptId).type(type).areaCode(e).build()));
        deptMapper.insertBatchDeptAreaRel(list);
        return true;
    }

    @Override
    public List<UserDeptRelationVo> queryUserDeptRelation(Long id) {
        return this.deptMapper.queryUserDeptRelation(id);
    }

    @Override
    public void insertUserDeptRelationBatch(List<UserDeptRelationDto> userDeptRelationDtoList) {
        this.deptMapper.insertUserDeptRelationBatch(userDeptRelationDtoList);
    }

    @Override
    public void deleteByUserIdAndDeptIds(Long userId, Collection<Long> deleteDeptIds) {
        this.deptMapper.deleteByUserIdAndDeptIds(userId, deleteDeptIds);
    }

    @Override
    public List<Tree<Long>> getUdrTree() {
        final Long userId = WebFrameworkUtils.getLoginUserId();
        final List<Long> userDeptRelationIds = deptMapper.getUserDeptRelationIds(userId);
        final List<DeptDO> deptTreeList = deptMapper.selectPurviewDeptTreeByDeptIds(userDeptRelationIds);
        return buildDeptTree(DeptTreeTypeEnum.USER,deptTreeList, GlobalConstants.TOP_TOP_DEPT_ID,true, userDeptRelationIds);
    }

    @Override
    @Cacheable(cacheNames = RedisCacheKey.SYSTEM_DEPT_TREE, key = "#searchVO.toString()",unless = "#searchVO.getPurview() == true or (#searchVO.getKeyword() != null and #searchVO.getKeyword() != '')")
    public List<Tree<Long>> getLazyTree(DeptTreeSearchVO searchVO) {
        if(searchVO.getParentId() == null) searchVO.setParentId(GlobalConstants.TOP_DEPT_ID);
        if(Optional.ofNullable(searchVO.getPurview()).orElse(false)){
            return buildPurviewTree(searchVO);
        }else{
            return buildDeptTree(searchVO);
        }
    }

    private List<Tree<Long>> buildDeptTree(DeptTreeSearchVO searchVO) {
        /**
         * 如果是用户机构树，判断管理范围权限是本单位or本单位及下级单位
         */
        if(Optional.ofNullable(searchVO.getUserAttr()).orElse(0) == 1 && !roleService.isSuperAdmin()){
                boolean hasSelfAndChildDeptDataScope = roleService.hasSelfAndChildDeptDataScope(WebFrameworkUtils.getLoginUserId());
                if(!hasSelfAndChildDeptDataScope){
                    DeptDO deptDO = deptMapper.selectDeptById(searchVO.getParentId());
                    List<DeptDO> list = Collections.singletonList(deptDO);
                    return buildDeptTree(DeptTreeTypeEnum.valueOf(searchVO),list,list.get(0).getParentId(),false, Lists.newArrayList());
                }
        }
        List<DeptDO> list;
        if(StringUtils.isNotBlank(searchVO.getKeyword())) {
            searchVO.setSubLevel(null);
            list = deptMapper.selectAllDeptTreeList(searchVO);
        } else {
            list = deptMapper.selectDeptTreeList(searchVO);
        }

        if(list.isEmpty()) return Lists.newArrayList();
        return buildDeptTree(DeptTreeTypeEnum.valueOf(searchVO),list, StringUtils.isNotBlank(searchVO.getKeyword()) ?
                        GlobalConstants.TOP_TOP_DEPT_ID : list.get(0).getParentId(), false, Lists.newArrayList());
    }

    private List<Tree<Long>> buildPurviewTree(DeptTreeSearchVO searchVO) {
        DeptDO deptDO = deptMapper.selectById(searchVO.getParentId());
        final Long userId = WebFrameworkUtils.getLoginUserId();
        final List<Long> purviewDeptIds = deptMapper.selectPurviewDeptIds(userId,searchVO.getTargetUserId(),searchVO.getUseFlag() == true?1:null,searchVO.getProjectAttr(),searchVO.getKeyword());

        final List<DeptDO> deptTreeList = deptMapper.selectPurviewDeptTreeByDeptIds(purviewDeptIds);
        return buildDeptTree(DeptTreeTypeEnum.valueOf(searchVO),deptTreeList,deptDO.getParentId(),true, purviewDeptIds);
    }

    private List<Tree<Long>> buildDeptTree(DeptTreeTypeEnum treeTypeEnum,List<DeptDO> result,Long parentId,boolean purview, List<Long> authDeptIds) {
        List<Tree<Long>> trees = new ArrayList<>();
        if (ToolUtil.isNotEmpty(result)) {
            TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
            treeNodeConfig.setIdKey("id");
            treeNodeConfig.setParentIdKey("parentId");
            treeNodeConfig.setChildrenKey("children");
            treeNodeConfig.setNameKey("name");
            trees = TreeUtil.build(result, parentId, treeNodeConfig, (dept, tree) -> {
                tree.setId(dept.getId());
                tree.setParentId(dept.getParentId());
                tree.setWeight(dept.getSort());
                tree.setName(dept.getName());
                if(result.size() == 1){
                    tree.putExtra("isLeaf",1);
                }else{
                    switch (treeTypeEnum){
                        case USER:
                            tree.putExtra("isLeaf", dept.getIsUserLeaf());
                            break;
                        case PROJECT:
                            tree.putExtra("isLeaf", dept.getIsProjectLeaf());
                            break;
                        case SYSTEM:
                            tree.putExtra("isLeaf", dept.getIsSystemLeaf());
                            break;
                        case FOUR4:
                            tree.putExtra("isLeaf", dept.getIs4aLeaf());
                            break;
                        default:
                            break;
                    }
                }
                tree.putExtra("fullPathId", dept.getFullPathId());
                tree.putExtra("useFlag", dept.getUseFlag());
                if(purview){
                    if(!authDeptIds.contains(dept.getId())){
                        tree.putExtra("disable", 1);
                    }else{
                        tree.putExtra("disable", 0);
                    }
                }
            });
        }
        return trees;
    }

    @Override
    public DeptDO getDeptByOrgCode(String orgCode) {
        LambdaQueryWrapperX<DeptDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.eq(DeptDO::getOrgCode,orgCode);
        queryWrapper.last("limit 1");
        return deptMapper.selectOne(queryWrapper);
    }

    @Override
    public List<Map<String, Object>> getUserDeptRelation(Long userId) {
        AdminUserDO user = this.userMapper.selectById(userId);
        if (ToolUtil.isEmpty(user)) {
            return new ArrayList<>();
        }
        List<Map<String, Object>> resultList = new ArrayList<>();
        Map<String, Object> result = new HashMap<>();
        DeptDO dept = deptMapper.selectById(user.getDeptId());
        result.put("deptId", dept.getId());
        result.put("deptName", dept.getName());
        resultList.add(result);
        List<UserDeptRelationVo> userDeptRelationVos = this.queryUserDeptRelation(userId);
        userDeptRelationVos.forEach(v -> {
            Map<String, Object> voResult = new HashMap<>();
            voResult.put("deptId", v.getDeptId());
            voResult.put("deptName", v.getDeptName());
            resultList.add(voResult);
        });

        return resultList;
    }

    @Override
    public void updateFullPathId(String pathFieldName) {
        LambdaQueryWrapperX<DeptDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.select(DeptDO::getId, DeptDO::getOrgId, DeptDO::getName, DeptDO::getParentId);
        List<DeptDO> deptList = deptMapper.selectList(queryWrapper);
        int count=0;
        for (DeptDO deptDO : deptList) {
            StringBuilder pathBuilder = new StringBuilder();
            LambdaUpdateWrapper<DeptDO> updateWrapper = new LambdaUpdateWrapper<>();
            Long id = deptDO.getId();

            SFunction<DeptDO, ?> fn;
            if("FULL_PATH_NAME".equalsIgnoreCase(pathFieldName)) {
                String name = deptDO.getName();
                pathBuilder.append(".").append(name);
                this.orgNamePath(deptDO.getParentId(), pathBuilder, pathFieldName);
                fn = DeptDO::getFullPathName;

            } else if("FULL_PATH_ORG_ID".equalsIgnoreCase(pathFieldName)) {
                String orgId = deptDO.getOrgId();
                pathBuilder.append(".").append(orgId);
                this.orgNamePath(deptDO.getParentId(), pathBuilder, pathFieldName);
                fn = DeptDO::getFullPathOrgId;

            } else {
                pathBuilder.append(".").append(id);
                this.idPath(id, pathBuilder);
                fn = DeptDO::getFullPathId;
            }
            updateWrapper.set(fn, pathBuilder.substring(1));
            updateWrapper.eq(DeptDO::getId, id);
            deptMapper.update(updateWrapper);

            System.out.println(pathBuilder.toString());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateFullPath() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        List<DeptDO> list = deptMapper.getDeptListWithParent();
        Map<Long,DeptDO> deptMap = list.stream().collect(Collectors.toMap(DeptDO::getId, Function.identity()));
        for(DeptDO deptDO : list){
            buildFullPath(deptDO, deptMap);
        }
        deptMapper.updateBatch(list,1000);
        stopWatch.stop();
        log.info("更新全路径耗时：{} s",stopWatch.getTotalTimeSeconds());
    }

    private void buildFullPath(DeptDO deptDO, Map<Long, DeptDO> deptMap) {
        DeptDO tempDeptDO = deptDO;
        StringBuilder idPathBuilder = new StringBuilder();
        StringBuilder namePathBuilder = new StringBuilder();
        while (true){
            idPathBuilder.insert(0,"." + tempDeptDO.getId());
            namePathBuilder.insert(0,"/" + tempDeptDO.getName());
            tempDeptDO = deptMap.get(tempDeptDO.getParentId());
            if(tempDeptDO == null) break;
        }
        deptDO.setFullPathId(idPathBuilder.insert(0,"0").toString());
        deptDO.setFullPathName(namePathBuilder.substring(1));
    }

    @Override
    public List<Long> getChildDeptTreeWithUserAttr(Long deptId) {
        return deptMapper.getChildDeptTreeWithUserAttr(deptId);
    }

    @Override
    public List<DeptDO> queryDeptList() {

        LambdaQueryWrapperX<DeptDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.eq( DeptDO::getDeleted, 0 );

        List<DeptDO> deptDOS = deptMapper.selectList(queryWrapper);

        return deptDOS;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateLeafProp() {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        List<DeptDO> list = deptMapper.selectList();
        Map<Long,DeptDO> deptMap = list.stream().collect(Collectors.toMap(DeptDO::getId, Function.identity()));
        for(int i=0,size=list.size();i<size;i=i+1000) {
            List<Long> ids = list.subList(i,i+1000 > size?size:i+1000).stream().map(DeptDO::getId).collect(Collectors.toList());
            List<Long> unLeafIds = deptMapper.selectLeafProp(ids,LeafProp.IS_4A_LEAF.getValue());
            unLeafIds.forEach(id -> {
                deptMap.get(id).setIs4aLeaf(0);
            });
            List<Long> leafIds = CollUtil.subtractToList(ids,unLeafIds);
            leafIds.forEach(id ->{
                deptMap.get(id).setIs4aLeaf(1);
            });

            unLeafIds = deptMapper.selectLeafProp(ids,LeafProp.IS_SYSTEM_LEAF.getValue());
            unLeafIds.forEach(id -> {
                deptMap.get(id).setIsSystemLeaf(0);
            });
            leafIds = CollUtil.subtractToList(ids,unLeafIds);
            leafIds.forEach(id ->{
                deptMap.get(id).setIsSystemLeaf(1);
            });

            unLeafIds = deptMapper.selectLeafProp(ids,LeafProp.IS_PROJECT_LEAF.getValue());
            unLeafIds.forEach(id -> {
                deptMap.get(id).setIsProjectLeaf(0);
            });
            leafIds = CollUtil.subtractToList(ids,unLeafIds);
            leafIds.forEach(id ->{
                deptMap.get(id).setIsProjectLeaf(1);
            });

            unLeafIds = deptMapper.selectLeafProp(ids,LeafProp.IS_USER_LEAF.getValue());
            unLeafIds.forEach(id -> {
                deptMap.get(id).setIsUserLeaf(0);
            });
            leafIds = CollUtil.subtractToList(ids,unLeafIds);
            leafIds.forEach(id ->{
                deptMap.get(id).setIsUserLeaf(1);
            });
        }
        deptMapper.updateBatch(deptMap.values(),1000);
        stopWatch.stop();
        log.info("更新部门叶子节点耗时：{} s",stopWatch.getTotalTimeSeconds());
    }

    @Override
    public List<Long> getChildDeptIds(DeptSearchVO searchVO) {
        return deptMapper.selectChildDeptIds(searchVO);
    }

    @Override
    public List<DeptVO> getUserDeptList(Long userId) {
        return deptMapper.selectUserDeptList(userId);
    }

    enum LeafProp {
        IS_4A_LEAF(1),
        IS_SYSTEM_LEAF(2),
        IS_PROJECT_LEAF(3),
        IS_USER_LEAF(4);

        private int value;

        LeafProp(int value) {
            this.value = value;
        }

        public int getValue() {
            return value;
        }
    }

    private void idPath(Long id, StringBuilder pathBuilder) {
        try{
            LambdaQueryWrapperX<DeptDO> queryWrapper = new LambdaQueryWrapperX<>();
            queryWrapper.eq(DeptDO::getId, id);
            queryWrapper.select(DeptDO::getParentId);
            DeptDO deptDO = deptMapper.selectOne(queryWrapper);
            if(deptDO != null && deptDO.getParentId()!=null){
                pathBuilder.replace(0, 1, "." + deptDO.getParentId() + ".");

                idPath(deptDO.getParentId(), pathBuilder);
            }
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    private void orgNamePath(Long parentId, StringBuilder pathBuilder, String pathFieldName) {
        LambdaQueryWrapperX<DeptDO> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.eq(DeptDO::getId, parentId);

        if("FULL_PATH_NAME".equalsIgnoreCase(pathFieldName))
            queryWrapper.select(DeptDO::getName, DeptDO::getParentId);
        else if("FULL_PATH_ORG_ID".equalsIgnoreCase(pathFieldName))
            queryWrapper.select(DeptDO::getOrgId, DeptDO::getParentId);
        else throw new RuntimeException("路径字段不存在");

        DeptDO deptDO = deptMapper.selectOne(queryWrapper);
        if(deptDO != null) {
            String replaceVal;
            if("FULL_PATH_NAME".equalsIgnoreCase(pathFieldName))
                replaceVal = deptDO.getName();
            else if("FULL_PATH_ORG_ID".equalsIgnoreCase(pathFieldName))
                replaceVal = deptDO.getOrgId();
            else replaceVal="";

            pathBuilder.replace(0, 1, "." + replaceVal + ".");
            orgNamePath(deptDO.getParentId(), pathBuilder, pathFieldName);
        }
    }

}
